#pragma once


#include <string>
#include <sstream>
#include <functional>

namespace helper {


/* 
 * Note:  Result._o == 0 => Result.isOK();
 *        Result._o > 0 => isError() == true;
 *        Result._o < 0 => isError() == true;
 *
 *     2. OK extend :
 *      result.isOK()== true 
 *        =>  okInfo() for extend , e.g. okcode in [1, 2, 3]
 */
template<typename E = int>
class Result {
public:
  typedef E ErrorType;


  //TESTCASE: Result(int o); result(o, E());
  //TESTCASE: Result(e); => Result(0, e);
  //TESTCASE: Result(o, e); => result(o, e);
  //TESTCASE: Result() => result(0, E());
  template<typename T>
  Result(const int o, const T & e)
  :_o(o), _e(e){
  }


  Result(const Result & r)
  :_o(r._o), _e(r._e){
  }
  Result & operator = (const Result & r){
    _o = r._o;
    _e = r._e;
    return *this;
  }

  virtual ~Result(){}

  bool isOK() const{
    return _o == 0;
  }
  const int & ok() const {
    return _o;
  }
  const E & okInfo() const{
    return error();
  }
  Result<E> & okInfo(const E & e) {
    //assert(_o == 0);
    _e = e;
    return *this;
  }


  bool isError() const {
    return !isOK();
  }
  const E &  error() const {
    return _e;
  }

  static Result OK(int o = 0) {
    return Result(o, E());
  }
  static Result Error(const E & e = E(), int oe = -1){
    return Result(oe, e);
  }

  virtual std::string toString() const {
    std::ostringstream oss;
    oss << "code:" << _o
        << "error:" << _e
        ;
    return oss.str();
  }

  bool operator ==(const Result & r) const {
    return _o == r._o && _e == r._e;
  }
  bool operator !=(const Result & r) const {
    return !(*this == r);
  }


  operator bool() const {
    return isOK();
  }
  








  //inspect  & inspectError
  Result<E> & inspectError(const std::function<void(const E &)> & op) {
    if(isError()){
      op(_e);
    }
    return *this;
  }
  Result<E> & inspect(const std::function<void(const int &)> & op) {
    if(isOK()){
      op(_o);
    }
    return *this;
  }

  //Note: E can be converted to type T
  //  this.isOK()    => return given value Result<T> result;
  //  this.isError() => return Result<T>::Error(_e);
  template<typename F>
  const Result<F> andReturn(const Result<F> & result) const{
    if(isOK()) return result;
    return Result<F>(_o, _e); //error self;
  }
  //Note: E can be converted to type F
  //  this.isOK() => return  op(_o) -> return Result<F>;
  //  this.isError() => return Result<F>::Error(_e);
  template<typename F>
  const Result<F> andThen(const std::function< const Result<F> (const int &)> & op_ok){
    if(isOK()) return op_ok(_o);
    return Result<F>(_o, _e); //error self
  }
  //Note: E can be converted to type F
	template<typename F>
	const Result<F> orReturn(const Result<F> & result) const {
		if(isError()) return result;
		return Result<F>(_o, _e); //ok self;
	}
  //Note: E can be converted to type F
  template<typename F>
  const Result<F> orElse(const std::function< const Result<F> (const E &)> & op_error){
    if(isError()) return op_error(_e);
    return Result<F>(_o, _e); //ok self
  }

	//map: Result<int,E> -> Result<int, E>
	Result<E> map(const std::function<int (const int &)> & op_ok) const {
		return Result<E>(op_ok(_o), _e);
	}
  //Result<int, E> -> Result<int, F>; customized type convertor op
  template<typename F>
  Result<F> mapError(const std::function<F (const E&)> & op_err) const {
    return Result<F>(_o, op_err(_e));
  }

	//map_or
	template<typename U>
	U mapOr(U & error, const std::function<U(const int&)> & op_ok) const {
		if(isOK()) return op_ok(_o);
		else return error;
	}
	//map_or_else
	template<typename U>
	U mapOrElse(const std::function<U(const E &)> & op_error, const std::function<U(const int &)> & op_ok) const {
		if(isOK()) return op_ok(_o);
		return op_error(_e);
	}

private:
  int _o;
  E _e;
};


typedef Result<std::pair<int, std::string>> IOResult;

}//namespace helper


namespace std {
  template<typename E>
  std::ostream & operator << (std::ostream & os, const helper::Result<E> & r) {
    os << r.toString() ;
    return os;
  }

	template<typename F, typename S> 
		std::ostream & operator << (std::ostream & os, const std::pair<F, S> & p){
			os << "pair:[" << p.first  << "," << p.second << "]";
			return os;
		}
}
